/*
 * {comment of class}
 *
 * @author (C) <a href="http://www.carfield.com.hk">Carfield Yim </a>, 2004
 */
package hk.com.carfield.core;

import hk.com.carfield.util.FileUtil;
import hk.com.carfield.util.StringUtil;
import hk.com.carfield.web.Request;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

public final class Config {
	public static Config getConfig(final File configFile) throws IOException {
		final Properties prop = new Properties();
		final InputStream is = new FileInputStream(configFile);
		prop.load(is);
		is.close();
		return new Config(prop, configFile.getParentFile());
	}

	public static Config getTestConfig() throws IOException {
		final String systemRoot = System.getProperty("user.dir");
		final String documentRoot = systemRoot + File.separator + "testdir";
		FileUtil.setupTestEnv(documentRoot);
		final Properties prop = new Properties();
		prop.put("document_root", documentRoot);
		prop.put("encoding", "UTF-8");
		final File dataRoot = new File(systemRoot, ".data");
		try {
			FileUtils.deleteDirectory(dataRoot);
		} catch (final Exception e) {
			// ignore
		}
		dataRoot.mkdirs();
		return new Config(prop, dataRoot);
	}

	public static boolean isSecure(final File file, final List<File> secureFiles) {
		final String path = file.getAbsolutePath();
		for (final Iterator<File> iter = secureFiles.iterator(); iter.hasNext();) {
			final File element = iter.next();
			if (path.indexOf(element.getAbsolutePath()) >= 0 && file.isFile()
			// need to check meta folder so that comments can be see
					&& !path.contains(FileUtil.metaFolderTag())) {
				return true;
			}
		}
		return false;
	}

	private final List<String> addresses;

	public final String headerFileName, readmeFileName, prefix;

	final Pattern ignoreFiles, indexFiles;

	public final int maxImageWidth;

	// TODO change to immutable map
	public final Map<String, String> nameMapping = new HashMap<String, String>();

	private final List<File> secureFiles;

	private final String smtpHost, adminEmail, adminPassword;

	public final float thumbnailHeigh;

	public final File webRoot, cachePath, dataRoot;

	private Config(final Properties prop, final File dataRoot) {
		this.dataRoot = dataRoot;
		webRoot = new File(prop.getProperty("document_root",
				System.getProperty("user.home")));
		cachePath = new File(webRoot, prop.getProperty("cache_path", ".cache"));
		cachePath.mkdirs();
		headerFileName = prop.getProperty("header_filename", "HEADER.html");
		readmeFileName = prop.getProperty("readme_filename", "README.html");
		final String w = prop.getProperty("webapp_name");
		prefix = StringUtil.notBlank(w) ? w : null;
		final String ignorePattern = prop.getProperty("index_ignore",
				".*[~#]$|.*,[vt]$")
				+ "|"
				+ headerFileName
				+ "|"
				+ readmeFileName;
		ignoreFiles = Pattern.compile(ignorePattern);
		indexFiles = Pattern
				.compile(prop
						.getProperty(
								"index_files",
								"index.html|index.php|index.php3|index.shtml|index.cgi|index.pl|index.htm|Default.htm|default.htm"));
		thumbnailHeigh = Float.parseFloat(prop.getProperty("thumbnail_height",
				"100"));
		maxImageWidth = Integer.parseInt(prop.getProperty("max_image_width",
				"855"));
		smtpHost = prop.getProperty("smtphost");
		adminEmail = prop.getProperty("admin_email");
		adminPassword = prop.getProperty("admin_password");

		final String[] paths = StringUtils.split(
				prop.getProperty("admin_directory"), "|");
		if (paths != null) {
			final File[] dirs = new File[paths.length];
			for (int i = 0; i < dirs.length; i++) {
				dirs[i] = new File(webRoot, paths[i]);
			}
			secureFiles = Collections.unmodifiableList(Arrays.asList(dirs));
		} else {
			secureFiles = new ArrayList<File>();
		}

		final String[] addresses = StringUtils.split(
				prop.getProperty("admin_directory"), "|");
		if (addresses != null) {
			this.addresses = Collections.unmodifiableList(Arrays
					.asList(addresses));
		} else {
			this.addresses = new ArrayList<String>();
		}

		// TODO need to think about how to reload config....
		for (final Object o : prop.keySet()) {
			final String key = (String) o;
			if (key.startsWith("rename")) {
				nameMapping.put(key.substring(6), prop.getProperty(key));
			}
		}
	}

	public boolean disallow(final String inetAddress) {
		// TODO actually not testable at all... need to think better solution
		return addresses.contains(inetAddress);
	}

	public FilenameFilter displayFilter() {
		return new FilenameFilter() {
			public boolean accept(final File dir, final String name) {
				final Matcher m1 = ignoreFiles.matcher(name);
				if (m1.matches() || name.startsWith(".")) {
					return false;
				} else {
					return true;
				}
			}
		};
	}

	public String getAdminEmail() {
		return adminEmail;
	}

	public String getAdminPassword() {
		return adminPassword;
	}

	public File getCacheDir() {
		return cachePath;
	}

	private String getFileContent(final File file) throws IOException {
		if (file.exists()) {
			final InputStreamReader is = new InputStreamReader(
					new FileInputStream(file), "UTF-8");
			final String str = StringUtil.text2Html(is);
			is.close();
			return str;
		} else {
			return "";
		}
	}

	public String getHeader(final File directory) throws IOException {
		return getFileContent(new File(directory, headerFileName));
	}

	public String getReadme(final File directory) throws IOException {
		return getFileContent(new File(directory, readmeFileName));
	}

	public File getRoot() {
		return webRoot;
	}

	public File getRoot(final String name) {
		return new File(webRoot, name);
	}

	public String getSmtpHost() {
		return smtpHost;
	}

	public FilenameFilter indexFilter() {
		return new FilenameFilter() {
			public boolean accept(final File dir, final String name) {
				final Matcher m = indexFiles.matcher(name);
				if (m.matches()) {
					return true;
				} else {
					return false;
				}
			}
		};
	}

	/**
	 * If this is a regular and acenstor exist in admin_directory config option,
	 * it won't show
	 */
	public boolean isSecure(final File file, final Request request) {
		return Config.isSecure(file, secureFiles) && !request.isAdminEnable();
	}
}